package top.rhynie.web3j.contract;

import com.google.protobuf.ByteString;
import org.tron.tronj.abi.FunctionReturnDecoder;
import org.tron.tronj.abi.TypeReference;
import org.tron.tronj.abi.datatypes.Address;
import org.tron.tronj.abi.datatypes.Bool;
import org.tron.tronj.abi.datatypes.Function;
import org.tron.tronj.abi.datatypes.Uint;
import org.tron.tronj.abi.datatypes.Utf8String;
import org.tron.tronj.abi.datatypes.generated.Uint256;
import org.tron.tronj.abi.datatypes.generated.Uint8;
import org.tron.tronj.client.TronClient;
import org.tron.tronj.client.transaction.TransactionBuilder;
import org.tron.tronj.proto.Chain;
import org.tron.tronj.proto.Response;
import org.tron.tronj.utils.Base58Check;
import org.tron.tronj.utils.Numeric;
import top.rhynie.web3j.Web3J;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ERC20 {

    private TronClient client;

    private ByteString ownerAddress;

    private ByteString contractAddress;

    private long feeLimit;

    private ERC20 (Web3J web3j, String ownerAddress, String contractAddress, long feeLimit) {
        this.client = web3j.getClient();
        this.ownerAddress = TronClient.parseAddress(ownerAddress);
        this.contractAddress = TronClient.parseAddress(contractAddress);
        this.feeLimit = feeLimit;
    }

    public static ERC20 load(Web3J web3j, String ownerAddress, String contractAddress, long feeLimit) {
        return new ERC20(web3j, ownerAddress, contractAddress, feeLimit);
    }

   public List<Object> allowance (
            String param0 ,
            String param1 
    ) {
        Function function = new Function("allowance",
                Arrays.asList(
                        new Address(param0 ),
                        new Address(param1 )
                ),
                Arrays.asList(
                    new TypeReference<Uint>() {}
                )

        );
            Response.TransactionExtention txnExt = client.constantCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()), Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            List<ByteString> constantResultList = txnExt.getConstantResultList();
            List<String> resultList = constantResultList.stream()
                .map(value -> Numeric.toHexString(value.toByteArray()))
                .collect(Collectors.toList());
            List<Object> returnValues = Stream.iterate(0, x -> ++x)
                .limit(resultList.size())
                .map(x -> FunctionReturnDecoder.decode(resultList.get(x), function.getOutputParameters()).get(x).getValue())
                .collect(Collectors.toList());
            return returnValues;
   }

   public boolean approve (
            String param0 ,
            BigInteger param1 
    ) {
        Function function = new Function("approve",
                Arrays.asList(
                        new Address(param0 ),
                        new Uint256(param1 )
                ),
                Arrays.asList(
                    new TypeReference<Bool>() {}
                )

        );
            TransactionBuilder builder = client.triggerCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()),
            Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            builder.setFeeLimit(feeLimit);
            Chain.Transaction signedTxn = client.signTransaction(builder.build());
            Response.TransactionReturn ret = client.broadcastTransaction(signedTxn);
            return ret.getResult();
   }

   public List<Object> balanceOf (
            String param0 
    ) {
      try {

      } catch (Exception e) {
        e.printStackTrace();
      }
        Function function = new Function("balanceOf",
                Arrays.asList(
                        new Address(param0 )
                ),
                Arrays.asList(
                    new TypeReference<Uint>() {}
                )

        );
            Response.TransactionExtention txnExt = client.constantCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()), Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            List<ByteString> constantResultList = txnExt.getConstantResultList();
            List<String> resultList = constantResultList.stream()
                .map(value -> Numeric.toHexString(value.toByteArray()))
                .collect(Collectors.toList());
            List<Object> returnValues = Stream.iterate(0, x -> ++x)
                .limit(resultList.size())
                .map(x -> FunctionReturnDecoder.decode(resultList.get(x), function.getOutputParameters()).get(x).getValue())
                .collect(Collectors.toList());
            return returnValues;
   }

   public List<Object> decimals (
    ) {
        Function function = new Function("decimals",
                    Collections.emptyList(),
                Arrays.asList(
                    new TypeReference<Uint8>() {}
                )

        );
            Response.TransactionExtention txnExt = client.constantCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()), Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            List<ByteString> constantResultList = txnExt.getConstantResultList();
            List<String> resultList = constantResultList.stream()
                .map(value -> Numeric.toHexString(value.toByteArray()))
                .collect(Collectors.toList());
            List<Object> returnValues = Stream.iterate(0, x -> ++x)
                .limit(resultList.size())
                .map(x -> FunctionReturnDecoder.decode(resultList.get(x), function.getOutputParameters()).get(x).getValue())
                .collect(Collectors.toList());
            return returnValues;
   }

   public List<Object> name (
    ) {
        Function function = new Function("name",
                    Collections.emptyList(),
                Arrays.asList(
                    new TypeReference<Utf8String>() {}
                )

        );
            Response.TransactionExtention txnExt = client.constantCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()), Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            List<ByteString> constantResultList = txnExt.getConstantResultList();
            List<String> resultList = constantResultList.stream()
                .map(value -> Numeric.toHexString(value.toByteArray()))
                .collect(Collectors.toList());
            List<Object> returnValues = Stream.iterate(0, x -> ++x)
                .limit(resultList.size())
                .map(x -> FunctionReturnDecoder.decode(resultList.get(x), function.getOutputParameters()).get(x).getValue())
                .collect(Collectors.toList());
            return returnValues;
   }

   public List<Object> symbol (
    ) {
        Function function = new Function("symbol",
                    Collections.emptyList(),
                Arrays.asList(
                    new TypeReference<Utf8String>() {}
                )

        );
            Response.TransactionExtention txnExt = client.constantCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()), Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            List<ByteString> constantResultList = txnExt.getConstantResultList();
            List<String> resultList = constantResultList.stream()
                .map(value -> Numeric.toHexString(value.toByteArray()))
                .collect(Collectors.toList());
            List<Object> returnValues = Stream.iterate(0, x -> ++x)
                .limit(resultList.size())
                .map(x -> FunctionReturnDecoder.decode(resultList.get(x), function.getOutputParameters()).get(x).getValue())
                .collect(Collectors.toList());
            return returnValues;
   }

   public List<Object> totalSupply (
    ) {
        Function function = new Function("totalSupply",
                    Collections.emptyList(),
                Arrays.asList(
                    new TypeReference<Uint>() {}
                )

        );
            Response.TransactionExtention txnExt = client.constantCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()), Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            List<ByteString> constantResultList = txnExt.getConstantResultList();
            List<String> resultList = constantResultList.stream()
                .map(value -> Numeric.toHexString(value.toByteArray()))
                .collect(Collectors.toList());
            List<Object> returnValues = Stream.iterate(0, x -> ++x)
                .limit(resultList.size())
                .map(x -> FunctionReturnDecoder.decode(resultList.get(x), function.getOutputParameters()).get(x).getValue())
                .collect(Collectors.toList());
            return returnValues;
   }

   public boolean transfer (
            String param0 ,
            BigInteger param1 
    ) {
        Function function = new Function("transfer",
                Arrays.asList(
                        new Address(param0 ),
                        new Uint256(param1 )
                ),
                Arrays.asList(
                    new TypeReference<Bool>() {}
                )

        );
            TransactionBuilder builder = client.triggerCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()),
            Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            builder.setFeeLimit(feeLimit);
            Chain.Transaction signedTxn = client.signTransaction(builder.build());
            Response.TransactionReturn ret = client.broadcastTransaction(signedTxn);
            return ret.getResult();
   }

   public boolean transferFrom (
            String param0 ,
            String param1 ,
            BigInteger param2 
    ) {
        Function function = new Function("transferFrom",
                Arrays.asList(
                        new Address(param0 ),
                        new Address(param1 ),
                        new Uint256(param2 )
                ),
                Arrays.asList(
                    new TypeReference<Bool>() {}
                )

        );
            TransactionBuilder builder = client.triggerCall(Base58Check.bytesToBase58(ownerAddress.toByteArray()),
            Base58Check.bytesToBase58(contractAddress.toByteArray()), function);
            builder.setFeeLimit(feeLimit);
            Chain.Transaction signedTxn = client.signTransaction(builder.build());
            Response.TransactionReturn ret = client.broadcastTransaction(signedTxn);
            return ret.getResult();
   }


}